{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Selecting data for use in Jupyter: Singapore taxi location data\n",
    "\n",
    "Often it's easier to use a visual application to draw a shape than define a geometry in code. Here we'll explore how pydeck can be used to select data and pass that selected data back to the Jupyter kernel for use in Pandas.\n",
    "\n",
    "Currently this notebook runs against live data, and there is a slight chance that the source data will be unavailable.\n",
    "\n",
    "## Contents\n",
    "\n",
    "- [Getting the data](#Getting-the-data)\n",
    "- [Plotting the data](#Plotting-the-data)\n",
    "- [Interaction](#Interaction)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Getting the data\n",
    "\n",
    "Here we'll use the live taxi location API provided by the government of Singapore."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pydeck as pdk\n",
    "\n",
    "DATA_URL = 'https://api.data.gov.sg/v1/transport/taxi-availability'\n",
    "COLOR_RANGE = [\n",
    "  [255, 255, 178, 25],\n",
    "  [254, 217, 118, 85],\n",
    "  [254, 178, 76, 127],\n",
    "  [253, 141, 60, 170],\n",
    "  [240, 59, 32, 212],\n",
    "  [189, 0, 38, 255]\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import requests\n",
    "\n",
    "json = requests.get(DATA_URL).json()\n",
    "df = pd.DataFrame(json[\"features\"][0][\"geometry\"][\"coordinates\"])\n",
    "df.columns = ['lng', 'lat']\n",
    "\n",
    "viewport = pdk.data_utils.compute_view(df[['lng', 'lat']])\n",
    "layer = pdk.Layer(\n",
    "    'ScreenGridLayer',\n",
    "    df,\n",
    "    get_position=['lng', 'lat'],\n",
    "    cell_size_pixels=20,\n",
    "    color_range=COLOR_RANGE,\n",
    "    pickable=True,\n",
    "    auto_highlight=True)\n",
    "r = pdk.Deck(layers=[layer], initial_view_state=viewport)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "r.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Two way communication\n",
    "\n",
    "Click the above visualization and then execute the cell below to pass data from the application to Python."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "r.selected_data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Event handlers\n",
    "\n",
    "You can also create more complex applications by drawing from the many widgets available within the [ipywidgets](https://ipywidgets.readthedocs.io/en/latest/index.html) library. Currently, pydeck supports a number of event handlers which can be linked to ipywidgets or elsewhere.\n",
    "\n",
    "Here, we'll use ipywidgets to tell us the number of taxis in the current viewport."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from ipywidgets import HTML\n",
    "\n",
    "text = HTML(value='Points in viewport:')\n",
    "layer = pdk.Layer('ScatterplotLayer', df, get_position=['lng', 'lat'], get_fill_color=[255, 0, 0], get_radius=100)\n",
    "r = pdk.Deck(layer, initial_view_state=viewport)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def filter_by_bbox(row, west_lng, east_lng, north_lat, south_lat):\n",
    "    return west_lng < row['lng'] < east_lng and south_lat < row['lat'] < north_lat\n",
    "\n",
    "\n",
    "def filter_by_viewport(widget_instance, payload):\n",
    "    try:\n",
    "        west_lng, north_lat = payload['data']['nw']\n",
    "        east_lng, south_lat = payload['data']['se']\n",
    "        filtered_df = df[df.apply(lambda row: filter_by_bbox(row, west_lng, east_lng, north_lat, south_lat), axis=1)]\n",
    "        text.value = 'Points in viewport: %s' % int(filtered_df.count()['lng'])\n",
    "    except Exception as e:\n",
    "        text.value = 'Error: %s' % e\n",
    "\n",
    "\n",
    "r.deck_widget.on_view_state_change(filter_by_viewport)\n",
    "display(text)\n",
    "r.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
